package com.cmc.cloud.cmclink.doc.service.impl.multimodaltransport;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;

import javax.annotation.Resource;

import org.apache.commons.collections.CollectionUtils;
import org.apache.tika.utils.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.cmc.cloud.cmclink.bdt.api.base.dto.resp.PortListByCodeRespDTO;
import com.cmc.cloud.cmclink.bdt.api.base.dto.resp.TerminalListByCodeRespDTO;
import com.cmc.cloud.cmclink.doc.api.document.emums.ExImTypeEnum;
import com.cmc.cloud.cmclink.doc.api.document.emums.SiValidEnum;
import com.cmc.cloud.cmclink.doc.api.document.emums.TransmodeEnum;
import com.cmc.cloud.cmclink.doc.configuration.CommonConfiguration;
import com.cmc.cloud.cmclink.doc.constants.BlnoFreightConstants;
import com.cmc.cloud.cmclink.doc.constants.CommonConstants;
import com.cmc.cloud.cmclink.doc.constants.errorconstant.ContainerManifestErrorCodeConstants;
import com.cmc.cloud.cmclink.doc.entity.*;
import com.cmc.cloud.cmclink.doc.mapper.*;
import com.cmc.cloud.cmclink.doc.rpcservice.BasicDataService;
import com.cmc.cloud.cmclink.doc.rpcservice.RouteTransfer;
import com.cmc.cloud.cmclink.doc.rpcservice.TerminalTransferService;
import com.cmc.cloud.cmclink.doc.service.multimodaltransport.ContainerManifestService;
import com.cmc.cloud.cmclink.doc.service.si.BlnoVslvoyService;
import com.cmc.cloud.cmclink.doc.service.so.SoService;
import com.cmc.cloud.cmclink.doc.util.CostChargeUtil;
import com.cmc.cloud.cmclink.doc.util.DateUtils;
import com.cmc.cloud.cmclink.doc.util.VslvoyUtil;
import com.cmc.cloud.cmclink.doc.vo.multimodaltransport.containermanifest.*;
import com.cmc.cloud.cmclink.doc.vo.sovo.sovo.SoQueryReqVO;
import com.cmc.cloud.cmclink.mkt.route.dto.ExtendRouteReqVO;
import com.cmc.cloud.cmclink.svl.api.base.req.BargePolPodReqDTO;
import com.cmc.cloud.cmclink.svl.api.base.req.BargeRotationCreateDTO;
import com.cmc.cloud.cmclink.svl.api.base.resp.BargePodRespDTO;
import com.cmc.cloud.cmclink.svl.api.base.resp.BargePolAndPodRespDTO;
import com.cmc.cloud.cmclink.svl.api.base.resp.BargePolRespDTO;
import com.cmc.cloud.cmclink.svl.api.base.resp.VesselVoyRespDTO;
import com.cmes.framework.common.exception.util.ServiceExceptionUtil;
import com.cmes.framework.common.pojo.PageResult;
import com.cmes.framework.mybatis.core.query.LambdaQueryWrapperX;

import lombok.extern.slf4j.Slf4j;

/**
 * 集装箱清单Impl
 * 
 * @author dev89
 * @date 2025/10/17 14:23
 **/
@Service
@Validated
@Slf4j
public class ContainerManifestServiceImpl implements ContainerManifestService {
    /**
     * 该提单未找到驳船
     */
    private static final String BLNO_BARGE_NOT_EXIST = "该提单未找到驳船";

    /**
     * 该提单已维护驳船
     */
    private static final String BLNO_BARGE_MAINTAIN_BARGE = "该提单已维护驳船";

    /**
     * TBA
     */
    private static final String BLNO_VSLVOY_DEFAULT_NAME = "TBA";

    /**
     * 该舱单非多式联运
     */
    private static final String MANIFEST_NOT_MULTIMODAL = "该舱单非多式联运";

    /**
     * yyyy/M/d
     */
    private static final String PATTERN1 = "yyyy-MM-dd";

    /**
     * yyyy-MM-dd HH:mm-ss
     */
    private static final String PATTERN2 = "yyyy-MM-dd HH:mm-ss";

    /**
     * 卸货港为CNSHA、CNTAO、CNRZH、CNTXG、CNDLC时，根据提单号列匹配舱单并绑定前程船名航次
     */
    private static final Set<String> MANIFEST_POD_PORT_SET =
        new HashSet<>(Arrays.asList("CNSHA", "CNTAO", "CNRZH", "CNTXG", "CNDLC"));

    @Resource
    private BlnoMapper blnoMapper;

    @Resource
    private BlnoVslvoyService blnoVslvoyService;
    @Resource
    private BlnoVslvoyMapper blnoVslvoyMapper;

    @Resource
    private ContainerManifestMapper containerManifestMapper;

    @Resource
    private SoMapper soMapper;

    @Resource
    private ManifestMapper manifestMapper;

    @Resource
    private CommonConfiguration commonConfiguration;

    @Resource
    private BasicDataService basicDataService;

    @Resource
    private TerminalTransferService terminalTransferService;

    @Resource
    private SoVslvoyMapper soVslvoyMapper;

    @Resource
    private MultiOpMapper multiOpMapper;

    @Resource
    private CostChargeUtil costChargeUtil;
    @Resource
    private SoService soService;

    @Resource
    private RouteTransfer routeTransfer;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importContainerManifest(ContainerManifestImportAllReqVO reqVo, String exImType) {
        List<ContainerManifestImportReqVO> containerReqList = reqVo.getContainerImportReqVoList();
        if (CollectionUtils.isEmpty(containerReqList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.BLNO_LIST_EMPTY);
        }

        Set<String> blnoSet =
            containerReqList.stream().map(ContainerManifestImportReqVO::getBlNo).collect(Collectors.toSet());
        List<SoDO> soDOList = getSoByManifestList(blnoSet);
        if (CollectionUtils.isEmpty(soDOList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.SO_EMPTY);
        }
        String name = reqVo.getVesselCode();
        String transmode = checkAndGetTransmode(soDOList, exImType);
        String bargeType = TransmodeEnum.valueOf(transmode).getBargeType();
        updateVesselCode(reqVo, bargeType);
        createIfNotExistBargeRotation(reqVo, exImType, name);
        String topAgent = checkAndGetTopAgent(soDOList);
        if (queryFromManifest(reqVo.getPodCode(), topAgent, exImType)) {
            importContainerFromManifest(blnoSet, reqVo.getVesselCode(), reqVo.getVoyage());
        } else {
            importContainerFromBlno(blnoSet, reqVo.getVesselCode(), reqVo.getVoyage(), exImType);
        }
    }

    private void createIfNotExistBargeRotation(ContainerManifestImportAllReqVO reqVo, String exImType, String name) {
        Set<String> blnoSet = reqVo.getContainerImportReqVoList().stream().map(ContainerManifestImportReqVO::getBlNo)
            .collect(Collectors.toSet());
        List<SoDO> soDOList = getSoByManifestList(blnoSet);
        String transmode = checkAndGetTransmode(soDOList, exImType);
        String bargeType = TransmodeEnum.valueOf(transmode).getBargeType();
        boolean exist;
        if (ExImTypeEnum.EX.getValue().equals(exImType)) {
            exist = terminalTransferService.existBargeRotation(bargeType, reqVo.getVesselCode(), reqVo.getPolCode(),
                reqVo.getVoyage(), null);
        } else {
            exist = terminalTransferService.existBargeRotation(bargeType, reqVo.getVesselCode(), reqVo.getPolCode(),
                null, reqVo.getVoyage());
        }

        if (!exist) {
            BargeRotationCreateDTO bargeRotation = buildBargeRotation(reqVo, name, bargeType);
            if (ExImTypeEnum.EX.getValue().equals(exImType)) {
                bargeRotation.setExVoy(reqVo.getVoyage());
            } else {
                bargeRotation.setImVoy(reqVo.getVoyage());
            }
            terminalTransferService.insertBargeRotation(bargeRotation);
        }
    }

    private void createIfNotExistBargeRotation(ContainerManifestImportCnshaAllReqVO reqVo, String name,
        String partnerCode) {
        Set<String> blnoSet = reqVo.getContainerImportReqVoList().stream()
            .map(ContainerManifestImportCnshaReqVO::getBlNo).collect(Collectors.toSet());
        List<SoDO> soDOList = getSoByManifestList(blnoSet);
        String transmode = checkAndGetTransmode(soDOList, ExImTypeEnum.IM.getValue());
        String bargeType = TransmodeEnum.valueOf(transmode).getBargeType();
        boolean exist = terminalTransferService.existBargeRotation(bargeType, reqVo.getVesselCode(),
            CommonConstants.CNSHA, null, reqVo.getVoyage());

        if (!exist) {
            String podCode = checkAndGetPodCode(soDOList, ExImTypeEnum.IM.getValue());
            BargeRotationCreateDTO bargeRotation =
                buildBargeRotation(reqVo, name, bargeType, partnerCode, podCode, transmode);
            terminalTransferService.insertBargeRotation(bargeRotation);
        }
    }

    private BargeRotationCreateDTO buildBargeRotation(ContainerManifestImportAllReqVO reqVo, String name,
        String bargeType) {
        BargeRotationCreateDTO bargeRotation = new BargeRotationCreateDTO();
        bargeRotation.setBargeType(bargeType);
        bargeRotation.setTransportNameEn(name);
        bargeRotation.setPortCode(reqVo.getPolCode());
        bargeRotation.setPartnerCode(reqVo.getPartnerCode());
        bargeRotation.setTerminalCode(reqVo.getPolTerminal());
        LocalDate etuDate = transitFromStr(reqVo.getEtu(), PATTERN1);
        LocalDate etbDate = transitFromStr(reqVo.getEtb(), PATTERN1);
        LocalDate atuDate = transitFromStr(reqVo.getAtu(), PATTERN1);
        LocalDate atbDate = transitFromStr(reqVo.getAtb(), PATTERN1);
        bargeRotation.setEta(etbDate);
        bargeRotation.setEtb(etbDate);
        bargeRotation.setEtd(etuDate);
        bargeRotation.setEtu(etuDate);
        bargeRotation.setAta(atbDate);
        bargeRotation.setAtb(atbDate);
        bargeRotation.setAtd(atuDate);
        bargeRotation.setAtu(atuDate);

        return bargeRotation;
    }

    private BargeRotationCreateDTO buildBargeRotation(ContainerManifestImportCnshaAllReqVO reqVo, String name,
        String bargeType, String partnerCode, String podCode, String transmode) {
        BargeRotationCreateDTO bargeRotation = new BargeRotationCreateDTO();
        bargeRotation.setBargeType(bargeType);
        bargeRotation.setTransportNameEn(name);
        bargeRotation.setPortCode(CommonConstants.CNSHA);
        bargeRotation.setPartnerCode(partnerCode);
        String polTerminal = getPolTerminalByPort(CommonConstants.CNSHA);
        bargeRotation.setTerminalCode(polTerminal);
        LocalDate atdDate = transitFromStr(reqVo.getAtd(), PATTERN2);
        int runtime = getBargeRuntime(CommonConstants.CNSHA, podCode, transmode);
        int day = (runtime + 23) / 24;
        LocalDate etaDate = atdDate.minusDays(day);
        bargeRotation.setEta(etaDate);
        bargeRotation.setEtb(etaDate);
        bargeRotation.setEtd(atdDate);
        bargeRotation.setEtu(atdDate);
        bargeRotation.setAtd(atdDate);
        return bargeRotation;
    }

    private LocalDate transitFromStr(String time, String pattern) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern);
        LocalDate localDate;
        try {
            localDate = LocalDate.parse(time, formatter);
        } catch (Exception e) {
            log.error("caught an exception, transitFromStr: {}, pattern: {}", time, pattern, e);
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.STRING_TO_LOCAL_DATE_EXCEPTION);
        }
        return localDate;
    }

    private void importContainerFromBlno(Set<String> blnoSet, String vesselCode, String voyage, String exImType) {
        List<BlnoDO> blnoDoList = getBlnoList(blnoSet);
        Set<Long> ids = blnoDoList.stream().map(BlnoDO::getId).collect(Collectors.toSet());
        List<BlnoVslvoyDO> blnoVslvoyDOList = blnoVslvoyService.getBlnoVslvoyBySiIdList(ids);
        // map<blnoId, list>
        Map<Long, List<BlnoVslvoyDO>> vslvoyListMap =
            blnoVslvoyDOList.stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
        List<BlnoVslvoyDO> updateDbVslvoyList = new ArrayList<>();
        vslvoyListMap.forEach((id, vslvoyList) -> {
            BlnoVslvoyDO vslvoyBarge = getBargeVslvoy(vslvoyList, exImType);
            vslvoyBarge.setVesselCode(vesselCode);
            vslvoyBarge.setVoyageEx(voyage);
            vslvoyBarge.setVoyageIm(voyage);
            updateDbVslvoyList.add(vslvoyBarge);
        });

        blnoVslvoyMapper.updateBatch(updateDbVslvoyList);
    }

    private void importContainerFromManifest(Set<String> blnoSet, String vesselCode, String voyage) {
        List<ManifestDO> manifestDOList = getManifestList(blnoSet);
        manifestDOList.forEach(manifest -> {
            manifest.setPreVessel(vesselCode);
            manifest.setPreVoyage(voyage);
        });
        manifestMapper.updateBatch(manifestDOList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void importContainerManifestCnsha(ContainerManifestImportCnshaAllReqVO reqVo) {
        List<ContainerManifestImportCnshaReqVO> containerReqList = reqVo.getContainerImportReqVoList();
        if (CollectionUtils.isEmpty(containerReqList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.BLNO_LIST_EMPTY);
        }
        Set<String> blnoSet =
            containerReqList.stream().map(ContainerManifestImportCnshaReqVO::getBlNo).collect(Collectors.toSet());
        String name = reqVo.getVesselCode();
        String partnerCode = checkAndGetPartnerCode(blnoSet);
        List<SoDO> soDOList = getSoByManifestList(blnoSet);
        if (CollectionUtils.isEmpty(soDOList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.SO_EMPTY);
        }

        String transmode = checkAndGetTransmode(soDOList, ExImTypeEnum.IM.getValue());
        String bargeType = TransmodeEnum.valueOf(transmode).getBargeType();
        updateVesselCode(reqVo, partnerCode, bargeType);
        createIfNotExistBargeRotation(reqVo, name, partnerCode);

        importContainerFromBlno(blnoSet, reqVo.getVesselCode(), reqVo.getVoyage(), ExImTypeEnum.IM.getValue());
    }

    @Override
    public List<ContainerManifestImportReqVO> checkContainerManifest(ContainerManifestImportAllReqVO reqVo,
        String exImType) {
        checkPartner(reqVo.getPartnerCode());
        checkPortAndTerminal(reqVo.getPolCode(), reqVo.getPolTerminal());
        checkPortAndTerminal(reqVo.getPodCode(), reqVo.getPodTerminal());
        Set<String> blnoSet = reqVo.getContainerImportReqVoList().stream().map(ContainerManifestImportReqVO::getBlNo)
            .collect(Collectors.toSet());
        List<SoDO> soDOList = getSoByManifestList(blnoSet);
        if (CollectionUtils.isEmpty(soDOList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.SO_EMPTY);
        }
        String transmode = checkAndGetTransmode(soDOList, exImType);
        String bargeType = TransmodeEnum.valueOf(transmode).getBargeType();
        updateVesselCode(reqVo, bargeType);
        String topAgent = checkTransmodeAndTopAgent(reqVo, exImType);
        boolean queryManifest = queryFromManifest(reqVo.getPodCode(), topAgent, exImType);
        log.info("is check from manifest: {}", queryManifest);
        if (queryManifest) {
            checkContainerFromManifest(reqVo);
        } else {
            checkContainerManifestCommon(reqVo.getContainerImportReqVoList(), exImType);
        }
        return reqVo.getContainerImportReqVoList();
    }

    private void checkPartner(String partnerCode) {
        basicDataService.getPartnerByCode(partnerCode);
    }

    private void updateVesselCode(ContainerManifestImportAllReqVO reqVo, String bargeType) {
        String vesselCode =
            terminalTransferService.getBargeVesselCode(reqVo.getVesselCode(), reqVo.getPartnerCode(), bargeType);
        reqVo.setVesselCode(vesselCode);
    }

    private void updateVesselCode(ContainerManifestImportCnshaAllReqVO reqVo, String partnerCode, String bargeType) {
        String vesselCode = terminalTransferService.getBargeVesselCode(reqVo.getVesselCode(), partnerCode, bargeType);
        reqVo.setVesselCode(vesselCode);
    }

    @Override
    public List<ContainerManifestImportCnshaReqVO>
        checkContainerManifestCnsha(ContainerManifestImportCnshaAllReqVO reqVo) {
        checkPortAndTerminal(CommonConstants.CNSHA, null);
        if (CollectionUtils.isEmpty(reqVo.getContainerImportReqVoList())) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.BLNO_LIST_EMPTY);
        }
        Set<String> blnoSet = reqVo.getContainerImportReqVoList().stream()
            .map(ContainerManifestImportCnshaReqVO::getBlNo).collect(Collectors.toSet());
        String partnerCode = checkAndGetPartnerCode(blnoSet);
        List<SoDO> soDOList = getSoByManifestList(blnoSet);
        if (CollectionUtils.isEmpty(soDOList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.SO_EMPTY);
        }
        String transmode = checkAndGetTransmode(soDOList, ExImTypeEnum.IM.getValue());
        String bargeType = TransmodeEnum.valueOf(transmode).getBargeType();
        updateVesselCode(reqVo, partnerCode, bargeType);
        checkTransmodeAndTopAgent(reqVo, ExImTypeEnum.IM.getValue());
        checkContainerManifestCommon(reqVo.getContainerImportReqVoList(), ExImTypeEnum.IM.getValue());
        return reqVo.getContainerImportReqVoList();
    }

    private boolean queryFromManifest(String podCode, String topAgent, String exImType) {
        if (ExImTypeEnum.IM.getValue().equals(exImType)) {
            return false;
        }

        if (!StringUtils.isEmpty(podCode) && MANIFEST_POD_PORT_SET.contains(podCode)) {
            return true;
        }

        if (!CommonConstants.CNNGB.equals(podCode)) {
            return false;
        }
        Set<String> topAgentSet = new HashSet<>(commonConfiguration.getTopAgent());
        return topAgentSet.contains(topAgent);
    }

    private String checkTransmodeAndTopAgent(ContainerManifestImportAllReqVO reqVo, String exImType) {
        if (CollectionUtils.isEmpty(reqVo.getContainerImportReqVoList())) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.BLNO_LIST_EMPTY);
        }
        Set<String> blnoSet = reqVo.getContainerImportReqVoList().stream().map(ContainerManifestImportReqVO::getBlNo)
            .collect(Collectors.toSet());
        List<SoDO> soDOList = getSoByManifestList(blnoSet);
        if (CollectionUtils.isEmpty(soDOList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.SO_EMPTY);
        }
        checkAndGetTransmode(soDOList, exImType);
        return checkAndGetTopAgent(soDOList);
    }

    private void checkTransmodeAndTopAgent(ContainerManifestImportCnshaAllReqVO reqVo, String exImType) {
        if (CollectionUtils.isEmpty(reqVo.getContainerImportReqVoList())) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.BLNO_LIST_EMPTY);
        }
        Set<String> blnoSet = reqVo.getContainerImportReqVoList().stream()
            .map(ContainerManifestImportCnshaReqVO::getBlNo).collect(Collectors.toSet());
        List<SoDO> soDOList = getSoByManifestList(blnoSet);
        if (CollectionUtils.isEmpty(soDOList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.SO_EMPTY);
        }
        checkAndGetTransmode(soDOList, exImType);
        checkAndGetTopAgent(soDOList);
    }

    private String checkAndGetTransmode(List<SoDO> soDOList, String exImType) {
        List<SoVslvoyDO> soVslvoyDOList = getBargeVslvoyList(soDOList, exImType);
        String transmode = "";
        for (SoVslvoyDO vslvoy : soVslvoyDOList) {
            String tmpTransmode = vslvoy.getTransmode();
            if (StringUtils.isEmpty(tmpTransmode)) {
                continue;
            }
            if (StringUtils.isEmpty(transmode)) {
                transmode = tmpTransmode;
            } else if (!transmode.equals(tmpTransmode)) {
                throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.TRANSMODE_NOT_INCONSISTENT);
            }
        }
        if (StringUtils.isEmpty(transmode)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.TRANSMODE_EMPTY);
        }
        return transmode;
    }

    private String checkAndGetTopAgent(List<SoDO> soDOList) {
        String topAgent = "";
        for (SoDO so : soDOList) {
            String tmpAgent = so.getTopAgent();
            if (StringUtils.isEmpty(tmpAgent)) {
                continue;
            }
            if (StringUtils.isEmpty(topAgent)) {
                topAgent = tmpAgent;
            } else if (!topAgent.equals(tmpAgent)) {
                throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.TOP_AGENT_NOT_INCONSISTENT);
            }
        }
        return topAgent;
    }

    private String checkAndGetPodCode(List<SoDO> soDOList, String exImType) {
        List<SoVslvoyDO> soVslvoyDOList = getBargeVslvoyList(soDOList, exImType);
        String podCode = "";
        for (SoVslvoyDO vslvoy : soVslvoyDOList) {
            String tmpPodCode = vslvoy.getTransmode();
            if (StringUtils.isEmpty(tmpPodCode)) {
                continue;
            }
            if (StringUtils.isEmpty(podCode)) {
                podCode = tmpPodCode;
            } else if (!podCode.equals(tmpPodCode)) {
                throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.POD_CODE_NOT_INCONSISTENT);
            }
        }
        if (StringUtils.isEmpty(podCode)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.POD_CODE_EMPTY);
        }
        return podCode;
    }

    private List<SoVslvoyDO> getBargeVslvoyList(List<SoDO> soDOList, String exImType) {
        Set<Long> soIdSet = soDOList.stream().map(SoDO::getId).collect(Collectors.toSet());
        LambdaQueryWrapperX<SoVslvoyDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(SoVslvoyDO::getSoId, soIdSet);
        if (ExImTypeEnum.EX.getValue().equals(exImType)) {
            queryWrapperX.eq(SoVslvoyDO::getRouteNo, 1);
            queryWrapperX.ne(SoVslvoyDO::getTransmode, TransmodeEnum.LINER.getValue());
        } else {
            queryWrapperX.ne(SoVslvoyDO::getRouteNo, 1);
            queryWrapperX.ne(SoVslvoyDO::getTransmode, TransmodeEnum.LINER.getValue());
        }
        return soVslvoyMapper.selectList(queryWrapperX);
    }

    private List<SoDO> getSoByManifestList(Set<String> blnoSet) {
        LambdaQueryWrapperX<SoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(SoDO::getBlNo, blnoSet);
        return soMapper.selectList(queryWrapperX);
    }

    private List<ManifestDO> getManifestList(Set<String> blnoSet) {
        LambdaQueryWrapperX<ManifestDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(ManifestDO::getManifestNo, blnoSet);
        return manifestMapper.selectList(queryWrapperX);
    }

    private void checkContainerFromManifest(ContainerManifestImportAllReqVO reqVo) {
        List<ContainerManifestImportReqVO> containerReqList = reqVo.getContainerImportReqVoList();
        Set<String> blnoSet =
            containerReqList.stream().map(ContainerManifestImportReqVO::getBlNo).collect(Collectors.toSet());
        List<ManifestDO> manifestDOList = getManifestList(blnoSet);
        Map<String, String> errMsgMap = new HashMap<>();
        // 校验提单是否已绑定驳船
        checkManifestAssociateBarge(manifestDOList, errMsgMap);
        containerReqList.forEach(container -> {
            String blno = container.getBlNo();
            container.setMsg(errMsgMap.getOrDefault(blno, CommonConstants.RESPONSE_SUCCESS));
        });
    }

    private void checkContainerManifestCommon(List<? extends ContainerManifestImportBaseReqVO> containerReqList,
        String exImType) {
        if (CollectionUtils.isEmpty(containerReqList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.BLNO_LIST_EMPTY);
        }
        Map<String, List<ContainerManifestImportBaseReqVO>> containerMap =
            containerReqList.stream().collect(Collectors.groupingBy(ContainerManifestImportBaseReqVO::getBlNo));
        Set<String> blnoSet = containerMap.keySet();
        Map<String, String> errMsgMap = new HashMap<>();
        // 校验提单是否已绑定驳船
        checkBlnoAssociateBarge(blnoSet, errMsgMap, exImType);
        containerReqList.forEach(container -> {
            String blno = container.getBlNo();
            container.setMsg(errMsgMap.getOrDefault(blno, CommonConstants.RESPONSE_SUCCESS));
        });
    }

    @Override
    public PageResult<ContainerManifestRespVO> getContainerManifestPage(ContainerManifestPageReqVO pageVo,
        String exImType) {
        IPage<ContainerManifestRespVO> page = Page.of(pageVo.getPageNo(), pageVo.getPageSize());
        IPage<ContainerManifestRespVO> pageResult;
        if (ExImTypeEnum.EX.getValue().equals(exImType)) {
            if (pageVo.getQueryMethod() == null || pageVo.getQueryMethod() == 0) {
                pageResult = containerManifestMapper.getContainerManifestPage(page, pageVo);
            } else {
                pageResult = containerManifestMapper.getContainerPageFormManifest(page, pageVo);
            }
        } else {
            pageResult = containerManifestMapper.getContainerManifestPageIm(page, pageVo);
        }
        List<ContainerManifestRespVO> respList = pageResult.getRecords();
        setPageResp(respList, exImType);
        return new PageResult<>(respList, pageResult.getTotal());
    }

    private void setPageResp(List<ContainerManifestRespVO> respList, String exImType) {
        if (CollectionUtils.isEmpty(respList)) {
            return;
        }

        for (ContainerManifestRespVO resp : respList) {
            if (resp == null) {
                log.error("ContainerManifestRespVO is null");
                continue;
            }
            setLinePage(resp, exImType);
            setBargePage(resp, exImType);
        }
    }

    private void setLinePage(ContainerManifestRespVO resp, String exImType) {
        List<String> vesselCodeList = new ArrayList<>();
        vesselCodeList.add(resp.getLineVesselCode());
        VesselVoyRespDTO vesselResp = null;
        try {
            vesselResp =
                costChargeUtil.getVoyQueryRespDTO(resp.getLinePolCode(), resp.getLineVoyageEx(), null, vesselCodeList);
        } catch (Exception e) {
            log.error("caught an exception on setLinePage", e);
        }
        if (vesselResp != null) {
            resp.setLinePolTerminal(vesselResp.getTerminalCode());
            resp.setLineEtd(vesselResp.getEtd() == null ? vesselResp.getPtd() : vesselResp.getEtd());
        }

        if (ExImTypeEnum.EX.getValue().equalsIgnoreCase(exImType)) {
            return;
        }
        VesselVoyRespDTO vesselImResp = null;
        try {
            vesselImResp =
                costChargeUtil.getVoyQueryRespDTO(resp.getLinePodCode(), resp.getLineVoyageIm(), null, vesselCodeList);
        } catch (Exception e) {
            log.error("caught an exception on setLinePage Im", e);
        }
        if (vesselImResp != null) {
            resp.setLinePodTerminal(vesselImResp.getTerminalCode());
        }
    }

    private void setBargePage(ContainerManifestRespVO resp, String exImType) {
        String bargeVesselCode = resp.getBargeVesselCode();
        if (StringUtils.isEmpty(bargeVesselCode) || BLNO_VSLVOY_DEFAULT_NAME.equalsIgnoreCase(bargeVesselCode)) {
            return;
        }

        if (StringUtils.isEmpty(resp.getBargeTransmode())) {
            resp.setBargeTransmode(getTransmodeFrom(resp.getBookingNo()));
        }
        String transmode = resp.getBargeTransmode();
        if (StringUtils.isEmpty(transmode)) {
            return;
        }
        String bargeType = TransmodeEnum.valueOf(transmode).getBargeType();

        BargePolPodReqDTO reqDto = new BargePolPodReqDTO();
        reqDto.setBargeType(bargeType);
        reqDto.setPol(resp.getBargePolCode());
        reqDto.setPod(resp.getBargePodCode());
        reqDto.setPolExVoy(resp.getBargeVoyage());
        Map<String, String> dateRage = DateUtils.getDateRange(1, 1);
        reqDto.setDateType("ETD");
        reqDto.setStartDate(dateRage.get(CommonConstants.DATE_TIME_START));
        reqDto.setEndDate(dateRage.get(CommonConstants.DATE_TIME_END));
        BargePolAndPodRespDTO respDto = null;
        try {
            respDto = terminalTransferService.getBargePolAndPod(reqDto);
        } catch (Exception e) {
            log.error("caught an exception on getBargePolAndPod", e);
        }
        if (respDto == null) {
            log.error("getBargePolAndPod enable response null");
        } else {
            BargePolRespDTO polRespDTO = respDto.getPolRespDTO();
            BargePodRespDTO podRespDTO = respDto.getPodRespDTO();
            if (polRespDTO != null) {
                resp.setBargePolTerminal(respDto.getPolRespDTO().getTerminalCode());
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                resp.setBargeAtu(transitDateToStr(polRespDTO.getAtu(), formatter));
                resp.setBargeAtb(transitDateToStr(polRespDTO.getAtb(), formatter));
                resp.setBargeEtu(transitDateToStr(polRespDTO.getEtu(), formatter));
                resp.setBargeEtb(transitDateToStr(polRespDTO.getEtb(), formatter));
            }
            if (podRespDTO != null) {
                resp.setBargePodTerminal(respDto.getPodRespDTO().getTerminalCode());
            }
        }
    }

    private String transitDateToStr(LocalDate localDate, DateTimeFormatter formatter) {
        if (localDate == null) {
            log.warn("get barge localDate null from line");
            return null;
        }
        String resp = null;
        try {
            resp = localDate.format(formatter);
        } catch (Exception e) {
            log.error("caught an exception on transitDateToStr, localDate: {}, formatter: {}", localDate, formatter, e);
        }
        return resp;
    }

    private String getTransmodeFrom(String bookingNo) {
        if (StringUtils.isEmpty(bookingNo)) {
            return null;
        }
        SoQueryReqVO soQueryReqVO = new SoQueryReqVO();
        soQueryReqVO.setBookingNo(bookingNo);
        SoDO soDO = soService.getSoDoByBookingNoOrBlNo(soQueryReqVO);
        Long soId = soDO.getId();
        LambdaQueryWrapperX<SoVslvoyDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(SoVslvoyDO::getId, soId).eq(SoVslvoyDO::getRouteNo, 1)
            .ne(SoVslvoyDO::getTransmode, TransmodeEnum.LINER.getValue()).last("limit 1");
        SoVslvoyDO soVslvoyDO = soVslvoyMapper.selectOne(queryWrapperX);
        if (soVslvoyDO == null) {
            return null;
        }
        return soVslvoyDO.getTransmode();
    }

    private void checkManifestAssociateBarge(List<ManifestDO> manifestDOList, Map<String, String> errMsgMap) {
        if (CollectionUtils.isEmpty(manifestDOList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.MANIFEST_EMPTY);
        }
        for (ManifestDO manifest : manifestDOList) {
            if (manifest.getPolCode().equals(manifest.getPorCode())) {
                errMsgMap.put(manifest.getManifestNo(), MANIFEST_NOT_MULTIMODAL);
                continue;
            }
            String preVoyage = manifest.getPreVoyage();
            if (preVoyage != null && !preVoyage.equals(BLNO_VSLVOY_DEFAULT_NAME)) {
                errMsgMap.put(manifest.getManifestNo(), BLNO_BARGE_MAINTAIN_BARGE);
            }
        }
    }

    private void checkBlnoAssociateBarge(Set<String> blnoSet, Map<String, String> errMsgMap, String exImType) {
        List<BlnoDO> blnoDoList = getBlnoChecks(blnoSet, errMsgMap);
        if (CollectionUtils.isEmpty(blnoDoList)) {
            return;
        }
        Set<Long> ids = blnoDoList.stream().map(BlnoDO::getId).collect(Collectors.toSet());
        Map<Long, String> idToBlnoMap =
            blnoDoList.stream().collect(Collectors.toMap(BlnoDO::getId, BlnoDO::getBlNo, (o1, o2) -> o1));
        List<BlnoVslvoyDO> blnoVslvoyDOList = blnoVslvoyService.getBlnoVslvoyBySiIdList(ids);
        // map<blnoId, list>
        Map<Long, List<BlnoVslvoyDO>> vslvoyListMap =
            blnoVslvoyDOList.stream().collect(Collectors.groupingBy(BlnoVslvoyDO::getBlnoId));
        vslvoyListMap.forEach((id, vslvoyList) -> {
            BlnoVslvoyDO vslvoyBarge = getBargeVslvoy(vslvoyList, exImType);
            if (vslvoyBarge == null) {
                errMsgMap.put(idToBlnoMap.get(vslvoyBarge.getBlnoId()), BLNO_BARGE_NOT_EXIST);
            } else {
                checkBargeExist(vslvoyList, vslvoyBarge, exImType, errMsgMap, idToBlnoMap);
            }
        });
    }

    private void checkBargeExist(List<BlnoVslvoyDO> vslvoyList, BlnoVslvoyDO vslvoyBarge, String exImType,
        Map<String, String> errMsgMap, Map<Long, String> idToBlnoMap) {
        boolean bargeExist = false;
        for (BlnoVslvoyDO vslvoy : vslvoyList) {
            boolean checkBarge;
            if (ExImTypeEnum.EX.getValue().equals(exImType)) {
                checkBarge = TransmodeEnum.LINER.getValue().equals(vslvoy.getTransmode())
                    && vslvoy.getPolCode().equals(vslvoyBarge.getPodCode());
            } else {
                checkBarge = TransmodeEnum.LINER.getValue().equals(vslvoy.getTransmode())
                    && vslvoy.getPodCode().equals(vslvoyBarge.getPolCode());
            }

            if (checkBarge) {
                bargeExist = true;
                String voyage = getVoyage(vslvoyBarge, exImType);
                if (!StringUtils.isEmpty(voyage) && !BLNO_VSLVOY_DEFAULT_NAME.equalsIgnoreCase(voyage)) {
                    errMsgMap.put(idToBlnoMap.get(vslvoyBarge.getBlnoId()), BLNO_BARGE_MAINTAIN_BARGE);
                }
                break;
            }
        }

        if (!bargeExist) {
            errMsgMap.put(idToBlnoMap.get(vslvoyBarge.getBlnoId()), BLNO_BARGE_NOT_EXIST);
        }
    }

    private String getVoyage(BlnoVslvoyDO vslvoy, String exImType) {
        if (ExImTypeEnum.EX.getValue().equals(exImType)) {
            return vslvoy.getVoyageEx();
        } else {
            return vslvoy.getVoyageIm();
        }
    }

    private BlnoVslvoyDO getBargeVslvoy(List<BlnoVslvoyDO> vslvoyList, String exImType) {
        if (ExImTypeEnum.EX.getValue().equals(exImType)) {
            return VslvoyUtil.getFirstNoLinerVslvoy(vslvoyList);
        } else {
            return VslvoyUtil.getLastNoLinerVslvoy(vslvoyList);
        }
    }

    private List<BlnoDO> getBlnoChecks(Set<String> blnoSet, Map<String, String> errMsgMap) {
        List<BlnoDO> blnoDoList = getBlnoList(blnoSet);
        Set<String> blnoDbs = blnoDoList.stream().map(BlnoDO::getBlNo).collect(Collectors.toSet());
        for (String blno : blnoSet) {
            if (StringUtils.isEmpty(blno) || !blnoDbs.contains(blno)) {
                errMsgMap.put(blno, BlnoFreightConstants.INVALID_ID);
            }
        }

        return blnoDoList;
    }

    private List<BlnoDO> getBlnoList(Set<String> blnoSet) {
        LambdaQueryWrapperX<BlnoDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(BlnoDO::getBlNo, blnoSet).eq(BlnoDO::getValid, SiValidEnum.VALID.getValue())
            .eq(BlnoDO::getPodBlFlag, true)
            .and(wrap -> wrap.isNull(BlnoDO::getDoStatus).or().eq(BlnoDO::getDoStatus, ""));
        return blnoMapper.selectList(queryWrapperX);
    }

    private int getBargeRuntime(String polCode, String podCode, String transmode) {
        ExtendRouteReqVO req = new ExtendRouteReqVO();
        req.setPor(polCode);
        req.setDes(podCode);
        req.setTransportMode(transmode);
        return routeTransfer.getTransportTime(req);
    }

    private String checkAndGetPartnerCode(Set<String> blnoSet) {
        List<BlnoDO> blnoDoList = getBlnoList(blnoSet);
        if (CollectionUtils.isEmpty(blnoDoList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.VALID_BLNO_EMPTY);
        }
        Set<Long> idSet = blnoDoList.stream().map(BlnoDO::getId).collect(Collectors.toSet());
        LambdaQueryWrapperX<MultiOpDO> queryWrapperX = new LambdaQueryWrapperX<>();
        queryWrapperX.in(MultiOpDO::getBlnoId, idSet);
        queryWrapperX.eq(MultiOpDO::getImExType, ExImTypeEnum.IM.getValue());
        queryWrapperX.eq(MultiOpDO::getOperationType, CommonConstants.BARGE_OPERATE_TYPE);
        List<MultiOpDO> multiOpDOList = multiOpMapper.selectList(queryWrapperX);
        if (CollectionUtils.isEmpty(multiOpDOList)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.PARTNER_TERMINAL);
        }
        long count = multiOpDOList.stream().map(MultiOpDO::getContent).distinct().count();
        if (count != 1) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.PARTNER_NOT_INCONSISTENT);
        }
        return multiOpDOList.get(0).getContent();
    }

    private void checkPortAndTerminal(String port, String terminalCode) {
        List<PortListByCodeRespDTO> portDtoList = basicDataService.getAllPorts();
        Set<String> portSet = portDtoList.stream().map(PortListByCodeRespDTO::getPortCode).collect(Collectors.toSet());
        if (!portSet.contains(port)) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.ILLEGAL_PORT);
        }

        List<TerminalListByCodeRespDTO> respList = terminalTransferService.queryTerminalByPortCode(port);
        if (StringUtils.isEmpty(terminalCode)) {
            return;
        }
        boolean existTerminal = respList.stream().anyMatch(resp -> terminalCode.equals(resp.getTerminalCode()));
        if (!existTerminal) {
            throw ServiceExceptionUtil.exception(ContainerManifestErrorCodeConstants.ILLEGAL_TERMINAL);
        }
    }

    private String getPolTerminalByPort(String port) {
        List<TerminalListByCodeRespDTO> respList = terminalTransferService.queryTerminalByPortCode(port);
        return respList.get(0).getTerminalCode();
    }
}
